/** \file logicparser.c
 * \brief Implementation of a simple logic proposition parser.
 * \author Marcelo Lira dos Santos
 * \date 2006-2007
 *
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License, see the file LICENSE.
 */
#include "logicparser.h"

/** \brief 'Disseca' uma string numa lista de tokens.
 * \param expr The expression string.
 * \return A linked list containing the tokens taken from the expression.
 */
GList *
lp_dissect(gchar *expr)
{
	GList *tokenList = NULL;
	LpToken *token = NULL;
	gint level = 0;
	gint i = 0;
	
	expr = g_ascii_strup (expr, -1);
	
	while (expr[i] != '\0') {

		/* Jump white spaces. */
		while (expr[i] == ' ' || expr[i] == '\t')
			i++;
		
		switch (expr[i]) {
			case C_LPAR:
			case C_RPAR:
				if (expr[i] == C_LPAR)
					level++;
				else
					level--;
				if (level < 0)
					return NULL;
			case C_AND:
			case C_OR:
			case C_NOT:
			case C_TRUE:
			case C_FALSE:
				token = g_new0(LpToken, 1);
				token->nodeType = expr[i];
				token->nodeId = NO_ID;
				token->level = level;
				if (expr[i] == C_LPAR)
					token->level--;
				tokenList = g_list_append(tokenList, LPTOKEN_TO_GPOINTER(token));
				break;
			case C_IF:
				i++;
				if (expr[i] == C_IF1) {
					token = g_new0(LpToken, 1);
					token->nodeType = C_IF;
					token->nodeId = NO_ID;
					token->level = level;
					tokenList = g_list_append(tokenList, LPTOKEN_TO_GPOINTER(token));
				} else {
					return NULL;
					token = g_new0(LpToken, 1);
					token->nodeType = C_ILLEGAL;
					token->nodeId = NO_ID;
					token->level = level;
					tokenList = g_list_append(tokenList, LPTOKEN_TO_GPOINTER(token));
				}
				break;
			case C_ID:
				i++;
				token = g_new0(LpToken, 1);
				if (expr[i] == '\0') {
					return NULL;
					token->nodeType = C_ILLEGAL;
					token->nodeId = NO_ID;
					token->level = level;
				} else {
					token->nodeType = C_ID;					
					token->nodeId = expr[i] - 48;
					token->level = level;
				}
				tokenList = g_list_append(tokenList, LPTOKEN_TO_GPOINTER(token));
				break;
			default:
				return NULL;
				token = g_new0(LpToken, 1);
				token->nodeType = C_ILLEGAL;
				token->nodeId = NO_ID;
				token->level = level;
				tokenList = g_list_append(tokenList, LPTOKEN_TO_GPOINTER(token));
		}
		i++;
	}

	return tokenList;
}

/** \brief Parses a expression in text form.
 * \param expr The expression string.
 * \return A root of the parsed tree, or NULL if there's a problem.
 */
LpNode *
lp_parse_expression (gchar *expr)
{
	if (expr == NULL)
		return NULL;
	else
		return lp_parse (lp_dissect (expr));
}

/** \brief Analisa a expressao e retorna uma 'parse tree' com raiz root.
 * \param tokLst A list of tokens.
 * \return A root of the parsed tree, or NULL if there's a problem.
 */
LpNode *
lp_parse (GList *tokLst)
{
	LpNode *root = NULL;
	GList *left, *right;
	GList *first, *last;
	
	if (tokLst == NULL)
		return NULL;

	first = g_list_first(tokLst);
	last = g_list_last(tokLst);
	
	/* remove parenteses inuteis */
	while (GPOINTER_TO_LPTOKEN(first->data)->nodeType == C_LPAR &&
			GPOINTER_TO_LPTOKEN(last->data)->nodeType == C_RPAR &&
			GPOINTER_TO_LPTOKEN(first->data)->level == 
				GPOINTER_TO_LPTOKEN(last->data)->level) {
		tokLst = g_list_delete_link(tokLst, first);
		tokLst = g_list_delete_link(tokLst, last);
		first = g_list_first(tokLst);
		last = g_list_last(tokLst);
	}
	tokLst = first;	
	
	left = first;
	if (GPOINTER_TO_LPTOKEN(left->data)->nodeType == C_LPAR ||
		GPOINTER_TO_LPTOKEN(left->data)->nodeType == C_ID ||
		GPOINTER_TO_LPTOKEN(left->data)->nodeType == C_FALSE ||
		GPOINTER_TO_LPTOKEN(left->data)->nodeType == C_TRUE ||
		GPOINTER_TO_LPTOKEN(left->data)->nodeType == C_NOT) {			
		while ((GPOINTER_TO_LPTOKEN(first->data)->nodeType != C_AND &&
			GPOINTER_TO_LPTOKEN(first->data)->nodeType != C_OR &&
			GPOINTER_TO_LPTOKEN(first->data)->nodeType != C_IF) ||
			GPOINTER_TO_LPTOKEN(first->data)->level != 
				GPOINTER_TO_LPTOKEN(left->data)->level) {
			first = g_list_next(first);
			if (first == NULL)
				break;
		}
		
		root = g_new0(LpNode, 1);
		if (first == NULL) {
			root->lpToken = GPOINTER_TO_LPTOKEN(left->data);
			if (GPOINTER_TO_LPTOKEN(left->data)->nodeType == C_NOT) {
				root->lpToken = GPOINTER_TO_LPTOKEN(left->data);
				left = g_list_delete_link(left, left);
				root->left = lp_parse(left);
			}
		} else {
			root->lpToken = GPOINTER_TO_LPTOKEN(first->data);

			if (first->prev != NULL)
				first->prev->next = NULL;
			else
				return NULL;

			if (first->next != NULL)
				first->next->prev = NULL;
			else
				return NULL;

			right = first->next;
			first->prev = NULL;
			first->next = NULL;
			first = g_list_delete_link(first, first);
			root->left = lp_parse(left);
			root->right = lp_parse(right);
		}
	} else 
		return NULL;

	return root;
}

/** \brief Returns a texual version of a token.
 * \param token A token representing an node in the parsed tree.
 * \return A string containing a textual version of the token.
 */
gchar *
lp_get_node_text(LpToken *token)
{
	gchar *text = NULL;

	switch (token->nodeType) {
		case C_AND:
			text = "and";
			break;
		case C_OR:
			text = "or";
			break;
		case C_IF:
			text = "if_then";
			break;
		case C_NOT:
			text = "not";
			break;
		case C_FALSE:
			text = "False";
			break;
		case C_TRUE:
			text = "True";
			break;
		case C_ID:
			text = g_strdup_printf("P%d", token->nodeId);
	}	
	
	return text;
}


